package register

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/sirupsen/logrus"
	"github.com/valyala/fasthttp"
	"log"
	"strings"
	"time"
)

type PullHandler struct {
	client *Client
}

type NodePull struct {
	Action string `json:"action"`
	Uuid   string `json:"uuid"`
	ToUuid string `json:"to_uuid"`
	Proto  int32  `json:"protocol"`
}

type PullRespsonse struct {
	PeerId         string    `json:"peer_id"`
	Sdp            NodeOffer `json:"sdp"`
	OfferId        int64     `json:"offer_id"`        //自己
	RemoteOfferId  int64     `json:"remote_offer_id"` //对方
	Proto          int32     `json:"protocol"`
	Setup          int       `json:"setup"` // 发起者1  ; 0
	PredictionPort int       `json:"prediction_port"`
}

const PullErrResp = `{"type":"error","errorcode":"4004","msg":"%s","uuid":"%s","to_uuid":"%s"}`

func (p *PullHandler) Handle(message []byte) {
	logrus.Info("[PullHandler.Handle] into ...")
	var state int
	pull := &NodePull{}
	touuidResp := PullRespsonse{}
	if err := json.Unmarshal(message, pull); err != nil {
		logrus.Info("[PullHandler.Handle] json.Unmarshal %s", err.Error())
		return
	}
	logrus.Info("[PullHandler.Handle] pull.uuid:%s, pull.to_uuid:%s", pull.ToUuid)
	uuidResp, uuidLocal, err := p.getNodeInfo(pull.ToUuid, 1)
	log.Printf("1212121212121212121212121212121212:%v",err)
	if err != nil {
		state |= 1 << 0
	}
	if state != 0 {
		type errPullRsponse struct {
			ErrorCode string `json:"errorcode"`
			Msg       string `json:"msg"`
			Uuid      string `json:"uuid"`
			ToUuid    string `json:"to_uuid"`
		}
		resp := &errPullRsponse{ErrorCode: "4004", Msg: "to_peer not exist", Uuid: pull.Uuid, ToUuid: pull.ToUuid}
		p.client.jsonResponse(resp)
		return
	}
	log.Printf("33333333333333333333333333333333330:%v,satte:%v", uuidLocal, state)
	touuidResp, toUuid, err := p.getNodeInfo(pull.Uuid, 0)
	if err != nil {
		state |= 1 << 1
	}
	log.Printf("33333333333333333333333333333333331:%v,satte:%v", toUuid, state)
	log.Printf("[info]:node pull state:%v", state)

	switch state {
	case 0:
		log.Printf("[info]: node pull 00 result:uuidresp:%v,pull uuid:%v", uuidResp, pull.Uuid)
		log.Printf("[info]: node pull 00 result:touuidresp:%v, pull to uuid:%v", touuidResp, pull.ToUuid)
		tmpOfferId := uuidResp.OfferId
		uuidResp.OfferId = touuidResp.OfferId
		touuidResp.OfferId = tmpOfferId
		if pull.Proto != 0 {
			proto := pull.Proto
			uuidResp.Proto = proto
			touuidResp.Proto = proto
		}
		log.Printf("[info]: node pull 01 result:uuidresp:%v", uuidResp)
		log.Printf("[info]: node pull 01 result:touuidresp:%v", touuidResp)
		err = p.client.jsonResponse(uuidResp)
		if err != nil {
			type errPullRsponse struct {
				ErrorCode string `json:"errorcode"`
				Msg       string `json:"msg"`
				Uuid      string `json:"uuid"`
				ToUuid    string `json:"to_uuid"`
			}
			resp := &errPullRsponse{ErrorCode: "4004", Msg: "to_peer not exist", Uuid: pull.Uuid, ToUuid: pull.ToUuid}
			p.client.jsonResponse(resp)
			return
		}
		log.Printf("uuidLocal 1:%v", uuidLocal.conn)
		err = uuidLocal.jsonResponse(touuidResp)

		if err != nil {
			type errPullRsponse struct {
				ErrorCode string `json:"errorcode"`
				Msg       string `json:"msg"`
				Uuid      string `json:"uuid"`
				ToUuid    string `json:"to_uuid"`
			}
			resp := &errPullRsponse{ErrorCode: "4004", Msg: "to_peer not exist", Uuid: pull.Uuid, ToUuid: pull.ToUuid}
			p.client.jsonResponse(resp)
		}
	case 1:
		//fastClient := &fasthttp.Client{
		//	ReadTimeout: 5 * time.Second,
		//}
		//req := fasthttp.AcquireRequest()
		//log.Printf("pull uuid:%s ", strings.ToLower(pull.Uuid))
		//const baseUrl = "/query?toUid=%s"
		//req.Header.SetMethod("GET")
		//req.SetRequestURI(fmt.Sprintf(baseUrl, strings.ToLower(pull.ToUuid)))
		//req.SetHost(p.client.server.remoteHost)
		////req.SetBody(ctx.Request.Body())
		//resp := fasthttp.AcquireResponse()
		//if err := fastClient.DoTimeout(req, resp, 100*time.Second); err != nil {
		//	log.Printf("[error]:node pull 1 query station:%v", err)
		//	return
		//}
		//if resp.StatusCode() != fasthttp.StatusOK {
		//	log.Printf("[waring]:node pull 1 query fail")
		//	return
		//}
		//var toPeer []byte
		//toPeer = resp.Header.Peek("toPeer")
		//log.Printf("node pull 1 query topeer:%s", string(toPeer))
		//uuidResp := new(PullRespsonse)
		pullResp := *p
		//if len(toPeer) != 0 {
		//	err = json.Unmarshal(toPeer, uuidResp)
			uuidResp.Setup = 1
			log.Printf("[info] node pull 1 uuid remote offer id:%v", touuidResp.RemoteOfferId)
		//if err != nil {
		//	log.Printf("node pull getNodeInfo pull.Uuid:%v", err)
		//	p.client.sendMessage(
		//		[]byte(fmt.Sprintf(PullErrResp, err.Error(), pull.Uuid, pull.ToUuid)),
		//	)
		//	return
		//}
			if touuidResp.PeerId != "" {
				log.Printf("[info]: node pull 10 result:uuidresp:%v,pull uuid:%v", uuidResp.RemoteOfferId, uuidResp.OfferId)
				log.Printf("[info]: node pull 10 result:touuidresp:%v, pull to uuid:%v", touuidResp.RemoteOfferId, touuidResp.OfferId)
				tmpOfferId := uuidResp.OfferId
				uuidResp.OfferId = touuidResp.OfferId
				touuidResp.OfferId = tmpOfferId
				log.Printf("[info]: node pull 11 result:uuidresp:%v,%v", uuidResp.RemoteOfferId, uuidResp.OfferId)
				log.Printf("[info]: node pull 11 result:touuidresp:%v,%v", touuidResp.RemoteOfferId, touuidResp.OfferId)
				if pull.Proto != 0 {
					proto := pull.Proto
					uuidResp.Proto = proto
					touuidResp.Proto = proto
				}
				p.client.jsonResponse(uuidResp)
				b, err := json.Marshal(touuidResp)
				if err != nil {
					logrus.Errorf("[Client.jsonResponse] Marshal err: %s", err.Error())
					return
				}
				go func(p PullHandler) {
					log.Printf("push to station server:%v", b)
					const baseUrl = "push?to=%s&from=%s"
					fastClient := &fasthttp.Client{
						ReadTimeout: 5 * time.Second,
					}
					req := fasthttp.AcquireRequest()
					req.Header.SetMethod("POST")
					req.SetRequestURI(fmt.Sprintf(baseUrl, strings.ToLower(string(pull.ToUuid)), strings.ToLower(string(pullResp.client.nodeAnnounce.PeerId))))
					req.SetHost(pullResp.client.server.remoteHost)
					req.SetBody(b)
					resp := fasthttp.AcquireResponse()
					err := fastClient.DoTimeout(req, resp, 10*time.Second)
					if err != nil && resp.StatusCode() != fasthttp.StatusOK {
						type errPullRsponse struct {
							ErrorCode string `json:"errorcode"`
							Msg       string `json:"msg"`
							Uuid      string `json:"uuid"`
							ToUuid    string `json:"to_uuid"`
						}
						pullResp.client.jsonResponse(errPullRsponse{ErrorCode: "4004", Msg: "no such node", Uuid: pullResp.client.nodeAnnounce.PeerId, ToUuid: pull.ToUuid})
					}
					log.Printf("node pull client resp:%v", string(resp.Body()))
					pullResp.client.byteResponse(resp.Body())
				}(pullResp)
			}
		//} else {
		//	type errPullRsponse struct {
		//		ErrorCode string `json:"errorcode"`
		//		Msg       string `json:"msg"`
		//		Uuid      string `json:"uuid"`
		//		ToUuid    string `json:"to_uuid"`
		//	}
		//	pullResp.client.jsonResponse(errPullRsponse{ErrorCode: "4004", Msg: "no such node", Uuid: pullResp.client.nodeAnnounce.PeerId, ToUuid: pull.ToUuid})
		//}
	case 2:
		fastClient := &fasthttp.Client{
			//ReadTimeout: 5*time.Second,
		}
		req := fasthttp.AcquireRequest()
		log.Printf("pull uuid:%s ", strings.ToLower(pull.Uuid))
		const baseUrl = "/query?uid=%s"
		req.Header.SetMethod("GET")
		req.SetRequestURI(fmt.Sprintf(baseUrl, strings.ToLower(pull.ToUuid)))
		req.SetHost(p.client.server.remoteHost)
		//req.SetBody(ctx.Request.Body())
		resp := fasthttp.AcquireResponse()
		if err := fastClient.DoTimeout(req, resp, 100*time.Second); err != nil {
			log.Printf("[error]:node pull 2 query station:%v", err)
			return
		}
		if resp.StatusCode() != fasthttp.StatusOK {
			log.Printf("[waring]:node pull 2 query fail")
			return
		}
		var peer []byte
		//var announce *NodeAnnounce
		peer = resp.Header.Peek("peer")
		log.Printf("node pull 2 query peer:%s", string(peer))
		touuidResp := new(PullRespsonse)
		if len(peer) != 0 {
			json.Unmarshal(peer, touuidResp)
			touuidResp.Setup = 0
			tmpOfferId := uuidResp.OfferId
			uuidResp.OfferId = touuidResp.OfferId
			touuidResp.OfferId = tmpOfferId
			log.Printf("[info]: node pull 2 result:uuidresp:%v", uuidResp)
			log.Printf("[info]: node pull 2 result:touuidresp:%v", touuidResp)
			p.client.jsonResponse(uuidResp)
			//p.client.server.clients[strings.ToLower(pull.ToUuid)].jsonResponse(touuidResp)
			toUuid.jsonResponse(touuidResp)
		}

	case 3:
		fastClient := &fasthttp.Client{
			//ReadTimeout: 5*time.Second,
		}
		req := fasthttp.AcquireRequest()
		log.Printf("pull uuid:%s,touuid:%s ", strings.ToLower(pull.Uuid), strings.ToLower(pull.ToUuid))
		const baseUrl = "/query?uid=%s&toUid=%s"
		req.Header.SetMethod("GET")
		req.SetRequestURI(fmt.Sprintf(baseUrl, strings.ToLower(pull.Uuid), strings.ToLower(pull.ToUuid)))
		req.SetHost(p.client.server.remoteHost)
		//req.SetBody(ctx.Request.Body())
		resp := fasthttp.AcquireResponse()
		if err := fastClient.DoTimeout(req, resp, 100*time.Second); err != nil {
			log.Printf("[eror]:node pull 3 query err:%v", err)
			return
		}
		if resp.StatusCode() != fasthttp.StatusOK {
			log.Printf("[waring]:node pull 3 query fail")
			return
		}
		var peer []byte
		var toPeer []byte
		peer = resp.Header.Peek("peer")
		toPeer = resp.Header.Peek("toPeer")
		log.Printf("node pull 3 query topeer:%s", string(toPeer))
		log.Printf("node pull 3 query peer:%s", string(peer))
		if len(peer) != 0 && len(toPeer) != 0 {
			json.Unmarshal(peer, touuidResp)
			json.Unmarshal(toPeer, uuidResp)
			touuidResp.Setup = 0
			uuidResp.Setup = 1
			tmpOfferId := uuidResp.OfferId
			uuidResp.OfferId = touuidResp.OfferId
			touuidResp.OfferId = tmpOfferId
			log.Printf("[info]: node pull 3 result:uuidresp:%v", uuidResp)
			log.Printf("[info]: node pull 3 result:touuidresp:%v", touuidResp)
			toUuid.jsonResponse(uuidResp)
			//p.client.server.clients[strings.ToLower(pull.ToUuid)].jsonResponse(touuidResp)
			b, err := json.Marshal(touuidResp)
			if err != nil {
				logrus.Errorf("[Client.jsonResponse] Marshal err: %s", err.Error())
				return
			}
			go func() {
				log.Printf("push to station server:%v", b)
				const baseUrl = "push?to=%s"
				fastClient := &fasthttp.Client{
					ReadTimeout: 5 * time.Second,
				}
				req := fasthttp.AcquireRequest()
				req.Header.SetMethod("POST")
				req.SetRequestURI(fmt.Sprintf(baseUrl, strings.ToLower(string(pull.ToUuid))))
				req.SetHost(p.client.server.remoteHost)
				req.SetBody(b)
				resp := fasthttp.AcquireResponse()
				if err := fastClient.DoTimeout(req, resp, 10*time.Second); err != nil {
					log.Printf("node push to server:%s", err)
				}
			}()
		}
	}
}

func (p *PullHandler) getNodeInfo(macAddr string, setup int) (PullRespsonse, Client, error) {
	nodeInfo := PullRespsonse{}
	client := Client{}
	p.client.server.lock.Lock()
	peer, ok := p.client.server.clients[strings.ToLower(macAddr)]

	if !ok {
		p.client.server.lock.Unlock()
		fastClient := &fasthttp.Client{
			ReadTimeout: 5 * time.Second,
		}
		req := fasthttp.AcquireRequest()
		log.Printf("pull uuid:%s ", strings.ToLower(macAddr))
		const baseUrl = "/query?toUid=%s"
		req.Header.SetMethod("GET")
		req.SetRequestURI(fmt.Sprintf(baseUrl, strings.ToLower(macAddr)))
		req.SetHost(p.client.server.remoteHost)
		//req.SetBody(ctx.Request.Body())
		resp := fasthttp.AcquireResponse()
		if err := fastClient.DoTimeout(req, resp, 100*time.Second); err != nil {
			log.Printf("[error]:node pull 1 query station:%v", err)
			return nodeInfo, peer, errors.New(fmt.Sprintf("GetHandler.getNodeInfo %s no such node", macAddr))
		}
		if resp.StatusCode() != fasthttp.StatusOK {
			log.Printf("[waring]:node pull 1 query fail")
			return nodeInfo, peer, errors.New(fmt.Sprintf("GetHandler.getNodeInfo %s no such node", macAddr))
		}
		var toPeer []byte
		toPeer = resp.Header.Peek("toPeer")

		log.Printf("45445545455454545454545454545454545454545445545:%v",toPeer)
		log.Printf("45445545455454545454545454545454545454545445545:%v",len(toPeer))
		log.Printf("node pull 1 query topeer:%s", string(toPeer))
		if len(toPeer) != 0 {
			err := json.Unmarshal(toPeer, nodeInfo)
			if err != nil {
				return nodeInfo, peer, errors.New(fmt.Sprintf("GetHandler.getNodeInfo %s no such node", macAddr))
			}
		}else {
			return nodeInfo, peer, errors.New(fmt.Sprintf("GetHandler.getNodeInfo %s no such node", macAddr))
		}
		return nodeInfo, peer, nil
	}

	sdpLen := len(peer.nodeAnnounce.Sdps)
	if sdpLen < 1 {
		p.client.server.lock.Unlock()
		return nodeInfo, client, errors.New(fmt.Sprintf("%s Sdps.len < 1 ", macAddr))
	}
	if time.Now().Unix()-peer.nodeAnnounce.Time > peer.nodeAnnounce.Timeout {
		p.client.server.lock.Unlock()
		return nodeInfo, peer,
			errors.New(fmt.Sprintf("GetHandler.getNodeInfo %s timeout", macAddr))
	}
	sdp := peer.nodeAnnounce.Sdps[0]
	peer.nodeAnnounce.Sdps = append(peer.nodeAnnounce.Sdps[:0], peer.nodeAnnounce.Sdps[1:]...)
	p.client.server.clients[strings.ToLower(macAddr)] = peer
	log.Printf("11111111111111111111111111111111111111111111:%v", p.client.server.clients[strings.ToLower(macAddr)].nodeAnnounce.Sdps)
	p.client.server.lock.Unlock()

	log.Printf("00000000000000000000000000000000000000000000:%v", sdp)

	nodeInfo.Setup = setup
	nodeInfo.PredictionPort = peer.nodeAnnounce.PredictionPort
	nodeInfo.PeerId = peer.nodeAnnounce.PeerId

	nodeInfo.Sdp = NodeOffer{
		Sdp:  sdp.Offer.Sdp,
		Type: sdp.Offer.Type,
	}
	nodeInfo.OfferId = sdp.OfferId
	nodeInfo.RemoteOfferId = sdp.RemoteOfferId
	//copy(peer.nodeAnnounce.Sdps, sdp[1:])
	remoteHost := p.client.server.remoteHost
	go func(host string) {
		const baseUrl = "sdp?client=%s"
		log.Printf("push to station delete 777777777777777777777777777777777777 :%v", strings.ToLower(macAddr))
		fastClient := &fasthttp.Client{
			//ReadTimeout: 5*time.Second,
		}
		req := fasthttp.AcquireRequest()
		req.Header.SetMethod("POST")
		//TODO client
		req.SetRequestURI(fmt.Sprintf(baseUrl, strings.ToLower(macAddr)))
		req.SetHost(host)
		resp := fasthttp.AcquireResponse()
		if err := fastClient.DoTimeout(req, resp, 10*time.Second); err != nil {
			log.Printf("[err]:(2)client send announce to transform server->%v", err)
		}
	}(remoteHost)
	return nodeInfo, peer, nil
}

